﻿###########################################
#
#  Into the GarlicScape
#
#  A roguelike game engine example for Octo
#
#  'Come, brave adventurer; don your woolen
#   armor and descend into the GarlicScape
#   in search of aliums. But beware- many
#   a moth or roach stands in your way!
#   Take solace in the Llamas. They will
#   heal your wounds, and ensure that you
#   have the strength to make it to
#   the fabled Alium Chalice of Yarnham.'
#
#  Move with ASWD or arrows,
#  and dismiss static screens with E or space.
#
#  This program uses XO-Chip instructions,
#  and expects to be run at around 500-1000
#  cycles/frame. The plane 2 and blend
#  colors should be identical to make
#  the player sprite opaque. Additionally,
#  the buzzer color should probably be set
#  to match the background.
#
###########################################

# reserved as temporaries: v0, v1, v3, vd, ve, vf
# still free (never touched) as globals: v8, v9, vA

:alias timer  vB # global continuous 0-8 animation counter
:alias input  vC # global buffered input key

# global game information that doesn't need to be in a register:

: game-transition 0                # defer certain actions until the top-level main loop
: current-map     0                # index of a prebuilt level (if any)
: player-health   0                # how many times can you get damaged before you die?
: player-score    0                # they call me gato / i have metal joints /

:const SCORE_GOAL               15 # beat me up / and earn 15 silver points
:const TRANSITION_GOTO_MAP       1 # travel to a different level/area
:const TRANSITION_DAMAGE_PLAYER  2 # injure the player at the end of animation...
:const TRANSITION_HEAL_PLAYER    3 # heal the player fully at the end of animation...
:const TRANSITION_ADD_SCORE      4 # increment the player's score at the end of animation...

# everything that isn't a static tile on the map, including the player, is a mob.
# mobs live in a global buffer here and share a common structure:

# type - id of the type of mob (see MOB_TYPE_XXX)
# x    - horizontal position in tiles
# y    - vertical position in tiles
# face - direction the mob is facing; either FACE_LEFT or FACE_RIGHT

:const MOB_COUNT 5 # how many mobs are allowed to spawn at once?
: mob-buffer
	0 0 0 0
	0 0 0 0
	0 0 0 0
	0 0 0 0
	0 0 0 0

# following from the above definitions, we have a calling
# convention used across many routines in this codebase:
:alias mob-index v2
:alias mob-type  v4
:alias mob-x     v5
:alias mob-y     v6
:alias mob-face  v7

: mob-get
	i := mob-buffer
	i += mob-index
	i += mob-index
	i += mob-index
	i += mob-index
;
: save-mob-regs mob-get save mob-type - mob-face ;
: load-mob-regs mob-get load mob-type - mob-face ;

# note that these line up with indices to mob-sprite-table:
:const MOB_TYPE_PLAYER 0
:const MOB_TYPE_LLAMA  1
:const MOB_TYPE_MOTH   2
:const MOB_TYPE_ROACH  3
:const MOB_TYPE_NONE  -1

# auxiliary/optional mob information, mostly used for
# animating mobs when they take a turn:
: mob-pow-table   0 0 0 0 0 # if 1, animate a flash over the mob indicating damage
: mob-hug-table   0 0 0 0 0 # if 1, float a heart from mob
: mob-dead-table  0 0 0 0 0 # if 1, destroy mob (after pow, cancels move)
: mob-roach-dir   0 0 0 0 0 # direction of a roach (0-3)
: mob-move-table  0 0 0 0 0 # direction to step (see deltas)
:const BUMP_OFFSET  4       #   OR direction + BUMP_OFFSET to bump in that direction and return
:const MOVE_NONE   -1       #   OR -1 for no move

# mob sprites have 4-frame animations, and are 7x7 pixels.
# the first 28 bytes are a right-facing animation,
# followed by a left-facing animation:
:const FACE_RIGHT 0
:calc  FACE_LEFT  { 7 * 4 }

# the macros 'sprite-begin' and 'sprite-end'
# encapsulate the process of building the mirrored copy
# of each frame of an animated sprite:
:macro sprite-begin {
	:calc sprite-base { HERE }
}
:macro mirror-row {
	:calc t { @ sprite-base }
	:byte { (   2 & t >> 6 ) | (  4 & t >> 4 ) | (  8 & t >> 2 ) | ( 16 & t ) |
	        ( 128 & t << 6 ) | ( 64 & t << 4 ) | ( 32 & t << 2 ) }
	:calc sprite-base { 1 + sprite-base }
}
:macro mirror-frame {
	mirror-row
	mirror-row
	mirror-row
	mirror-row
	mirror-row
	mirror-row
	mirror-row
}
:macro sprite-end {
	mirror-frame
	mirror-frame
	mirror-frame
	mirror-frame
}

# helpers for working with XO-Chip high RAM.
# the 'to-data' macro indicates that the ensuing data
# should be assembled into high RAM (which must be indexed
# with the i := long NNNN instruction),
# and the 'to-code' macro swaps back to assembling in low RAM.
# using these macros allows you to declare data before using it,
# while keeping data out of precious low RAM:
:calc CODE_POS { 0x200  }
:calc DATA_POS { 0x1000 }
:macro to-code { :calc DATA_POS { HERE }  :org { CODE_POS } }
:macro to-data { :calc CODE_POS { HERE }  :org { DATA_POS } }

# helpers for multiple memory indirection/pointers:
# unpack16 places a computed 16-bit address in v registers,
# pointer writes a computed 16-bit address into memory as a literal,
# and indirect creates an i := long NNNN instruction with a label
# halfway through, ready to be overwritten with a new 16-bit value:
:macro unpack16 ADDR {
	:calc hi { 0xFF & ADDR >> 8 }  v0 := hi
	:calc lo { 0xFF & ADDR      }  v1 := lo
}
:macro pointer ADDR {
	:byte { 0xFF & ADDR >> 8 }
	:byte { 0xFF & ADDR      }
}
:macro indirect LABEL {
	0xF0 0x00 : LABEL 0x00 0x00 # i := long NNNN
}

# misc:
:macro NOOP {
	# sometimes no-ops are desirable for breakpoints,
	# or to pad out a dispatch table.
	vf := vf
}
:macro set-times-7 DEST SRC {
	# dest/src MUST be distinct!
	DEST := SRC  # y
	DEST += DEST # y*2
	DEST += SRC  # y*3
	DEST += DEST # y*6
	DEST += SRC  # y*7
}
:macro swap A B {
	vf := A
	A := B
	B := vf
}
: wait
	loop
		vf := delay
		if vf != 0 then
	again
	vf := 3
	delay := vf
;
: buffer-input
	if input != -1 then return
	:macro buffer-test SCAN KEY {
		vf := SCAN
		if vf key then input := KEY
	}
	buffer-test OCTO_KEY_A 0
	buffer-test OCTO_KEY_D 1
	buffer-test OCTO_KEY_W 2
	buffer-test OCTO_KEY_S 3
;
: wait-for-space
	vf := OCTO_KEY_E
	loop if vf  key then again
	loop if vf -key then again
	loop if vf  key then again
;

###########################################
#
#  Sound Effects
#
###########################################

to-data

: sound-step   0x02 0x21 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
: sound-bump   0x02 0x21 0x00 0x08 0x66 0x00 0xD8 0x01 0xB0 0x20 0x10 0x10 0x00 0x00 0x00 0x00
: sound-pickup 0xFC 0x0F 0xC0 0xFC 0x0F 0xC0 0xF8 0x1F 0x81 0xF8 0x1F 0x81 0xF0 0x3F 0x03 0xF0
: sound-heal   0xE3 0x8E 0x38 0xE3 0x8E 0x38 0xC7 0x1C 0x71 0xC7 0x1C 0x71 0xCE 0x38 0xE3 0x8E
: sound-hurt   0xA5 0xDF 0x71 0xDC 0x50 0x1D 0x38 0x97 0x5A 0x7D 0xDF 0xB5 0xE5 0xF4 0x97 0xD5

to-code

# this is the simplest way to use the XO-Chip audio pattern buffer:
# brief, percussive patterns played in a set-and-forget manner.
# see Octo's Audio Editor in the toolbox to preview and edit these sounds.

: play-sound
	audio
	vf := 1
	buzzer := vf
;

:macro sfx SOUND {
	i := long SOUND
	play-sound
}

###########################################
#
#  Title Screen / Game Over
#
###########################################

to-data

# These screens were prepared using EZ-Pack:
# http://beyondloom.com/tools/ezpack.html
# starting with a 128x64 pixel black-and-white image,
# use default settings aside from changing
# "Sprite Size" to 0 for 16x16 sprites in each chunk.

: title-data
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x0F 0xFE 0x1F 0xFE 0x1F 0xF6
	0x1C 0x1A 0x1C 0x1C 0x1C 0x0E 0x1C 0x02 0x1C 0x00 0x1C 0x00 0x1C 0xFE 0x1D 0x1E
	0x1D 0x4E 0x1C 0x8E 0x1C 0x0E 0x1C 0x0E 0x1C 0x0E 0x1F 0xFE 0x1F 0xFE 0x0F 0xFC
	0x00 0x00 0x00 0x00 0x00 0x7F 0x00 0x80 0x00 0xC0 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x3F 0xC7 0x7F 0xE7 0x7F 0xE7
	0x70 0xE7 0x70 0xE7 0x70 0xE7 0x70 0xE7 0x70 0xE7 0x70 0xE7 0x7F 0xE7 0x7F 0xE7
	0x7F 0xE7 0x70 0xE7 0x70 0xE7 0x70 0xE7 0x70 0xE7 0x70 0xE7 0x70 0xE7 0xF9 0xF7
	0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x01 0x80 0x01 0x00 0x00 0xFF 0x00 0x00 0x00 0x00 0xFC 0x7E 0xFE 0x79 0xFE 0x72
	0x0E 0x70 0x0E 0x70 0x0E 0x70 0x0E 0x70 0x0E 0x70 0x1E 0x70 0xFC 0x70 0xF8 0x70
	0xFC 0x70 0x1E 0x70 0x0E 0x70 0x0E 0x70 0x0E 0x70 0x0E 0x7F 0x0E 0x7F 0x9F 0x7F
	0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xA5 0xDE 0xB4 0x92 0xAC 0x92
	0xA4 0x92 0xA4 0x92 0xA4 0x9E 0x00 0x00 0x00 0x00 0x1F 0x3F 0x0E 0x7F 0x0E 0x7F
	0x0E 0x70 0x0E 0x72 0x0E 0x71 0x0E 0x70 0x0E 0x70 0x0E 0x70 0x0E 0x70 0x0E 0x70
	0x0E 0x70 0x0E 0x71 0x8E 0x72 0x4E 0x70 0xCE 0x70 0xCE 0x7F 0xCE 0x7F 0xDF 0x3F
	0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x3A 0x5E 0x12 0x50 0x13 0xD8
	0x12 0x50 0x12 0x50 0x12 0x5F 0x00 0x00 0x00 0x00 0xC7 0xF8 0xEF 0xFD 0xEF 0xFD
	0xEE 0x1D 0xEE 0x1D 0xEE 0x1D 0x0E 0x5D 0x0E 0x3D 0x0E 0x01 0x0F 0xF9 0x0F 0xFD
	0x07 0xFD 0xE0 0x1D 0xE7 0xDD 0xEE 0x5D 0xEE 0x9D 0xEE 0x1D 0xEF 0xFD 0xC7 0xF8
	0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x01 0x80 0x00 0x80 0xFF 0x00 0x00 0x00 0x00 0x00 0xFF 0x0F 0xFF 0x9F 0xFF 0x9F
	0xC3 0x9C 0xCB 0x9C 0xC7 0x9C 0xC0 0x1C 0xC0 0x1C 0xC0 0x1C 0xC0 0x1F 0xC0 0x1F
	0xC0 0x1F 0xC7 0x9C 0xCB 0x9C 0xC3 0x9C 0xC3 0x9C 0xFF 0x9C 0xFF 0x9C 0xFF 0x3E
	0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xF0 0xFF 0xF9 0xFF 0xF9 0xFF
	0x39 0xE7 0x39 0xC3 0x39 0xC3 0x39 0xCB 0x39 0xD3 0x39 0xD3 0xF9 0xDF 0xF9 0xDF
	0xF9 0xCF 0x39 0xC0 0x39 0xC0 0x39 0xC0 0x39 0xC0 0x39 0xC0 0x39 0xC0 0x7D 0xE0
	0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x3F 0xF8 0x9F 0xF8 0x9F 0xD8
	0x9C 0x68 0x9C 0x30 0x9C 0x18 0x9C 0x08 0x9C 0xC0 0x9C 0x40 0x9F 0xC0 0x9F 0xC0
	0x1F 0xC0 0x1C 0x40 0x1C 0x10 0x1C 0x08 0x1C 0x18 0x1F 0xF8 0x1F 0xF8 0x3F 0xF8
	0x00 0x00 0x00 0x00 0xFE 0x00 0x01 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

: game-over-data
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xC0 0x00 0x80 0x00 0xFF 0x20 0x00 0x3F 0xFF
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x3F 0xFF 0x20 0x00 0x00 0xFF 0x00 0x80 0x00 0xC0 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0xFF 0xFF
	0x00 0x00 0x03 0xF3 0x00 0xC0 0x00 0x61 0x00 0x33 0x00 0x1E 0x00 0x0C 0x00 0x0C
	0x00 0x0C 0x00 0x0C 0x00 0x0C 0x00 0x0C 0x00 0x0C 0x00 0x0C 0x00 0x3F 0x00 0x00
	0xFF 0xFF 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0xFF 0xFF
	0x00 0x00 0xF1 0xC7 0xC3 0x61 0x86 0x31 0x06 0x31 0x0C 0x19 0x0C 0x19 0x0C 0x19
	0x0C 0x19 0x0C 0x19 0x0C 0x19 0x06 0x30 0x06 0x30 0x03 0x60 0x01 0xC0 0x00 0x00
	0xFF 0xFF 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0xFF 0xFF
	0x00 0x00 0xE7 0xE0 0x81 0x80 0x81 0x80 0x81 0x80 0x81 0x80 0x81 0x80 0x81 0x80
	0x81 0x80 0x81 0x80 0x81 0x80 0xC3 0x00 0xC3 0x00 0x66 0x00 0x3C 0x00 0x00 0x00
	0xFF 0xFF 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0xFF 0xFF
	0x00 0x00 0x3F 0x87 0x0C 0x61 0x0C 0x31 0x0C 0x31 0x0C 0x19 0x0C 0x19 0x0C 0x19
	0x0C 0x19 0x0C 0x19 0x0C 0x19 0x0C 0x31 0x0C 0x31 0x0C 0x61 0x3F 0x87 0x00 0x00
	0xFF 0xFF 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0xFF 0xFF
	0x00 0x00 0xEF 0xF9 0x83 0x18 0x83 0x0C 0x83 0x04 0x83 0x00 0x83 0x10 0x83 0xF0
	0x83 0x10 0x83 0x00 0x83 0x00 0x83 0x04 0x83 0x0C 0x83 0x18 0xEF 0xF9 0x00 0x00
	0xFF 0xFF 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00 0xFF 0xFF
	0x00 0x00 0xFC 0x00 0x63 0x00 0x61 0x80 0x61 0x80 0x60 0xC0 0x60 0xC0 0x60 0xC0
	0x60 0xC0 0x60 0xC0 0x60 0xC0 0x61 0x80 0x61 0x80 0x63 0x00 0xFC 0x00 0x00 0x00
	0xFF 0xFF 0x00 0x00 0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x01 0x00 0xFF 0x00 0x00 0x04 0xFF 0xFC
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0xFF 0xFC 0x00 0x04 0xFF 0x00 0x01 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

: you-win-data
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x1A 0x00 0x50 0x00 0x1F 0xFF 0x40 0x00
	0x7F 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x7F 0xFF 0x40 0x00
	0x1F 0xFF 0x50 0x00 0x1A 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x0D 0x00 0x13 0x00 0x08 0x00 0x04
	0x00 0x02 0x00 0x02 0x00 0x01 0x00 0x01 0x00 0x01 0x00 0x02 0x00 0x02 0x00 0x04
	0x00 0x19 0x00 0x23 0x00 0x47 0x00 0x9E 0x01 0x3D 0x02 0x79 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x00 0x00 0x7D 0xF7 0x10 0x43 0x10 0x43 0x18 0xC3 0x08 0x83 0x0D 0x83
	0x0D 0x83 0x0D 0x83 0x07 0x03 0x07 0x03 0x0F 0x87 0x00 0x00 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x45 0x9F 0x4B 0xBF 0x8F 0xBF 0x9F 0xBF 0x9F 0xDF 0x9F 0xDF 0x9F 0xDF
	0x97 0xEF 0x9F 0xFF 0x8B 0xFB 0x4F 0xFF 0x26 0xFF 0x23 0x3F 0x11 0x8F 0x0C 0x60
	0x02 0x00 0x01 0x80 0x00 0x7F 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0xC6 0x00 0xAA 0x00 0x92 0x00 0x12 0x00 0x02 0x00 0x49 0x00 0x29 0x00 0x2D 0x00
	0x54 0x80 0x16 0xC0 0x1A 0x38 0x2B 0x07 0x35 0xC0 0x57 0x70 0x57 0xFF 0xDF 0xFD
	0xBF 0xFF 0x7F 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x00 0x00 0x87 0x9F 0x18 0xD3 0x18 0x43 0x30 0x03 0x30 0x03 0x30 0x03
	0x30 0x03 0x30 0x03 0x18 0x43 0x18 0xC3 0x87 0x87 0x00 0x00 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0xFF 0xDF 0xFF 0xDF 0xFF 0xEF 0xFF 0xEF 0xFF 0xEF 0xFF 0xF7 0xFF 0xFB
	0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0x7E 0xFF 0x9F 0x3F 0xC0 0x07 0x00 0x00
	0x01 0xC0 0x06 0x49 0xF8 0xAC 0x00 0xA6 0x00 0x5B 0x00 0x04 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0xC0 0x00 0x3F 0xC0 0x00 0x38 0xC0 0x06 0xFF 0xC1
	0xBF 0xF0 0xEF 0xFC 0xF3 0xFF 0xFC 0xFF 0xFE 0x7E 0xFF 0x3F 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x00 0x00 0xE3 0x87 0x2C 0x63 0x0C 0x63 0x18 0x33 0x18 0x33 0x18 0x33
	0x18 0x33 0x18 0x33 0x0C 0x63 0x0C 0x63 0x83 0x87 0x00 0x00 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
	0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF 0xFE 0xFF 0xFC 0xFF 0xC0 0x00 0x00
	0x00 0x02 0x22 0x53 0xAD 0x4D 0xD7 0x6A 0xB6 0x95 0x4B 0x08 0x04 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x00
	0x40 0x00 0x20 0x00 0x10 0x00 0x88 0x00 0xC4 0x00 0x62 0x00 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x00 0x00 0xF9 0xCE 0x0C 0x84 0x0C 0x48 0x0C 0x48 0xF8 0x30 0x30 0x30
	0x18 0x30 0x18 0x30 0x0C 0x30 0x0C 0x30 0x9E 0x78 0x00 0x00 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x99 0xF2 0xDC 0xF9 0xDC 0xF9 0xDC 0xFA 0xB8 0xEA 0xB9 0xF4 0x71 0xC0
	0xE1 0x81 0xC0 0x02 0x80 0x1C 0x1F 0xE0 0x30 0x00 0x28 0x00 0x10 0x00 0xC8 0x00
	0x70 0x00 0x28 0x00 0xD0 0x00 0x40 0x00 0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0x00 0x00
	0xFF 0xFF 0x40 0x00 0x20 0x00 0x20 0x00 0x20 0x00 0x20 0x00 0x40 0x00 0x40 0x00
	0x80 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x58 0x00 0x0A 0xFF 0xF8 0x00 0x02
	0xFF 0xFE 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFE 0x00 0x02
	0xFF 0xF8 0x00 0x0A 0x00 0x58 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

: chalice-data
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01 0x00 0x02 0x00 0x06 0x00 0x07 0x00 0x07
	0x00 0x03 0x00 0x03 0x00 0x02 0x00 0x10 0x00 0x09 0x00 0x0D 0x00 0x16 0x00 0x6A
	0x01 0x6D 0x02 0x6E 0x02 0x75 0x03 0x7F 0x02 0xC7 0x01 0x38 0x00 0x80 0x00 0x70
	0x00 0x0F 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x03 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x18 0x00 0x60 0x00 0xE0 0x00 0x60 0x03 0xF0 0x01 0xF8 0x01 0x7E 0x01 0xFF 0x23
	0xC1 0x27 0x3E 0xEE 0x71 0xC9 0xEF 0xC3 0xDF 0xDF 0xDF 0xBE 0xDF 0xBD 0xFF 0xA3
	0xBF 0x9F 0x9F 0xBF 0xAF 0xBF 0xB3 0xBE 0x18 0x8E 0xD4 0x31 0x23 0x8E 0x00 0x00
	0xF0 0x00 0x5F 0xFF 0x34 0x01 0x0C 0xA9 0x0A 0xFA 0x0A 0xAA 0x0A 0x22 0x0A 0xFA
	0x0E 0x23 0x0A 0xFA 0x0A 0xFA 0x0A 0x22 0x0A 0xFA 0x0E 0xAB 0x0A 0x22 0x09 0xFC
	0x12 0x22 0x14 0xF9 0x34 0x21 0xC8 0xA8 0x90 0xA8 0xF0 0xF8 0x0E 0x23 0x01 0xFC
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x80 0x00 0xC0 0x00 0xE0 0x80 0xE3 0x00
	0xF5 0x00 0xEB 0x80 0x5B 0xC0 0x5B 0xC0 0x35 0xE0 0xB5 0xF0 0xB6 0x70 0xBB 0xB0
	0xBD 0xD4 0x1E 0xDA 0x2F 0xFA 0x60 0xF6 0xC7 0x1A 0x58 0xE4 0x20 0x08 0x00 0x70
	0x7F 0x80 0xD0 0x00 0x60 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0x80 0x00
	0x80 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0x80 0x00 0x80 0x00
	0x40 0x00 0x40 0x00 0x60 0x00 0x98 0x00 0x4E 0x00 0x78 0x00 0x80 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
	0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00

to-code

: screen-blit
	v0 := 0
	v1 := 0
	v2 := 16
	v3 := 32
	v4 := 48
	loop
		sprite v0 v1 0  i += v3
		sprite v0 v2 0  i += v3
		sprite v0 v3 0  i += v3
		sprite v0 v4 0  i += v3
		v0 += 16
		if v0 != 128 then
	again
;

:macro title-screen     { i := long title-data     screen-blit }
:macro game-over-screen { i := long game-over-data screen-blit }
:macro you-win-screen   { i := long you-win-data   screen-blit }
:macro chalice-screen   { i := long chalice-data   screen-blit }

###########################################
#
#  HUD
#
###########################################

:const MAX_HEALTH 3
:const HEALTH_METER_X 1
:const HEALTH_METER_Y 1

: health-draw
	plane 1
	v0 := HEALTH_METER_X
	v1 := HEALTH_METER_Y
	i := player-health
	load v2 - v2
	i := long smallheart
	loop
		while v2 != 0
		sprite v0 v1 5
		v1 += 6
		v2 += -1
	again
;

:const SCORE_METER_X 123
:const SCORE_METER_Y   1

: score-digits 0 0 0
: score-draw
	i := player-score
	load v0
	i := score-digits
	bcd v0
	load v2
	i := hex v0
	v3 := SCORE_METER_X
	v0 := SCORE_METER_Y
	sprite v3 v0 5
	v0 += 6
	i := hex v1
	sprite v3 v0 5
	v0 += 6
	i := hex v2
	sprite v3 v0 5
;

: health-decrement
	i := player-health
	load v2 - v2
	if v2 == 0 then return
	v2 += -1
	save v2 - v2

	v0 := HEALTH_METER_X
	v1 := v2
	v1 += v1 # * 2
	v1 += v2 # * 3
	v1 += v1 # * 6
	v1 += HEALTH_METER_Y
	plane 2
	i := long smallheart
	v2 := 4
	loop
		sprite v0 v1 5
		wait
		v2 += -1
		if v2 != 0 then
	again
	plane 1
	sprite v0 v1 5
;

: score-increment
	plane 1
	score-draw
	i := player-score
	load v2 - v2
	v2 += 1
	save v2 - v2
	plane 2
	ve := 4
	loop
		score-draw
		wait
		ve += -1
		if ve != 0 then
	again
	plane 1
	score-draw
;

: health-reset
	i := player-health
	vf := MAX_HEALTH
	save vf - vf
;
: score-reset
	i := player-score
	vf := 0
	save vf - vf
;

###########################################
#
#  The Game Board
#
###########################################

to-data

# tiles are 7x7, but last row/col is always empty.
# tiles are drawn in color, and stored as premultiplied offsets
# into the tile sprites. this makes drawing them much more efficient.
# this encoding currently limits maps to 21 distinct tiles.

# this macro provides shorthands for building maps as well
# as longer named constants for game logic for each tile/mob spawn
:const TILE_SIZE 12
:macro tile-def NAME SYMBOL {
	:calc  NAME   { TILE_SIZE * CALLS }
	:macro SYMBOL { :byte NAME }
}
:macro mob-def NAME SYMBOL {
	:calc  NAME   { - 1 + CALLS }
	:macro SYMBOL { :byte NAME }
}
: tiles
	0x00 0x00 0x00 0x00 0x00 0x00  0x00 0x00 0x00 0x00 0x00 0x00  tile-def TILE_NOTHING     N
	0x00 0x00 0x00 0x00 0x10 0x00  0x00 0x00 0x00 0x00 0x00 0x00  tile-def TILE_FLOOR       .
	0xFC 0x04 0xBC 0x10 0xFC 0xB4  0x00 0x00 0x00 0x00 0x00 0x00  tile-def TILE_WALL        W
	0x00 0x00 0x00 0x00 0x00 0x00  0x78 0xFC 0x74 0xF4 0x7C 0xFC  tile-def TILE_DOOR        D
	0x00 0x00 0x00 0x00 0x00 0x00  0xFC 0x84 0xC4 0xD4 0xD4 0xFC  tile-def TILE_STAIRS_DOWN s
	0xC0 0xD0 0x14 0xC4 0xF0 0xFC  0x00 0x00 0x00 0x00 0x00 0x00  tile-def TILE_STAIRS_UP   S
	0x00 0x00 0x00 0x00 0x00 0x00  0x38 0x50 0xA8 0xB4 0xB4 0x68  tile-def TILE_GARLIC      g

mob-def TILE_PLAYER  P
mob-def TILE_LLAMA   L
mob-def TILE_MOTH    m
mob-def TILE_ROACH_L d
mob-def TILE_ROACH_R b
mob-def TILE_ROACH_U ^
mob-def TILE_ROACH_D v

:const BOARD_WIDTH  16
:const BOARD_HEIGHT  9
:calc  BOARD_SIZE   { BOARD_WIDTH * BOARD_HEIGHT }
:calc  BOARD_END    { -1 + BOARD_SIZE }

: board # 16x9 on SCHIP rez, row-major
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

# levels are copied to the 'board' buffer before
# drawing/gameplay, so that the board may be
# modified in-place and later restored.

: level-0 # the title screen
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	0 0 0 0 W W W W W W W W 0 0 0 0
	0 0 0 0 W P . . . . s W 0 0 0 0
	0 0 0 0 W W W W W W W W 0 0 0 0
: level-1 # introduction
	0 0 0 g g 0 0 0 0 . 0 0 g 0 g 0
	0 0 g 0 0 g 0 . 0 0 0 g g 0 0 0
	0 m 0 0 0 0 0 0 0 0 0 0 0 0 0 0
	. 0 0 W W W W W W W W W W 0 . 0
	0 . 0 W P . . . . . . s W v 0 0
	0 0 0 W W W W W W W W W W 0 0 0
	0 g 0 0 0 0 . g g 0 0 0 0 . g g
	0 . g g 0 0 0 0 0 0 m 0 0 g g g
	0 0 g g 0 . 0 0 0 0 0 g 0 0 g g
: level-2 # unused/test level.
	0 0 0 0 0 0 0 0 0 0 0 0 0 W W W
	0 0 0 0 W W W W W W W 0 0 W s W
	W W W W W m . . . . W W W W . W
	W . . . W . . W . . . . . W . W
	W . P . W . . . . . . . . . . W
	W . S . . . . W W W W . m W W W
	W L . . W . . W 0 0 W W W W 0 0
	W W W W W g g W 0 0 0 0 0 0 0 0
	0 0 0 0 W W W W 0 0 0 0 0 0 0 0
: level-3 # conclusion
	0 0 g 0 . 0 0 g g 0 W 0 0 0 0 0
	0 . 0 g W W W W W W W 0 0 0 0 0
	g 0 g g W . . . . . . 0 0 0 0 0
	0 W W W W . W . . . 0 0 0 0 0 0
	0 W S P . . . . . . . 0 0 0 0 0
	0 W W W W . W . . . 0 0 0 0 0 0
	0 0 g g W . . . . . . 0 0 0 0 0
	0 . 0 g W W W W W . . . . g . 0
	0 0 g 0 0 . g g W W W W W W 0 0

: map-table
	pointer level-0
	pointer level-1
	pointer level-2
	pointer level-3

to-code

# how far to the right do we start drawing the game board?
# this leaves some space at the edges for a HUD...
:const HORIZ_OFFSET 8
:const VERT_OFFSET  1

: unpack-mob
	# reserve slot 0 for the player,
	# so that we can use the slot index to easily
	# identify/access the player elsewhere:
	if mob-type == MOB_TYPE_PLAYER begin
		vd := mob-index
		mob-index := 0
		mob-face := 0
		save-mob-regs
		mob-index := vd
	else
		if mob-index != MOB_COUNT begin
			mob-face := random 1
			if mob-face != 0 then mob-face := FACE_LEFT
			save-mob-regs
			mob-index += 1
		end
	end
	v1 := TILE_FLOOR
;

# by unpacking in a separate pass from drawing,
# we take more time overall, but do a better job
# of hiding the copy from users- the copy can
# take place entirely during a blank screen:
: board-unpack
	i := current-map
	load v0
	i := long map-table
	i += v0
	i += v0
	load v1
: board-unpack-raw
	i := board-unpack-source
	save v0 - v1

	# clear mob slots
	mob-index := 0
	mob-type := MOB_TYPE_NONE
	loop
		save-mob-regs
		mob-index += 1
		if mob-index != MOB_COUNT then
	again

	v0        := 0 # tile offset
	mob-index := 1 # mob index for unpacking
	mob-x     := 0 # tile x
	mob-y     := 0 # tile y

	loop
		indirect board-unpack-source
		i += v0
		load v1 - v1
		:macro unpack-mob-tile TILE {
			if v1 == TILE begin
				:calc type { CALLS }
				mob-type := type
				unpack-mob
			end
		}
		unpack-mob-tile TILE_PLAYER 
		unpack-mob-tile TILE_LLAMA
		unpack-mob-tile TILE_MOTH

		# roaches are special, because they always
		# begin facing some direction:
		:macro unpack-roach-tile TILE DIR {
			if v1 == TILE begin
				i := mob-roach-dir
				i += mob-index
				vf := DIR
				save vf - vf
				mob-type := MOB_TYPE_ROACH
				unpack-mob
			end
		}
		unpack-roach-tile TILE_ROACH_L 0
		unpack-roach-tile TILE_ROACH_R 1
		unpack-roach-tile TILE_ROACH_U 2
		unpack-roach-tile TILE_ROACH_D 3

		i := long board
		i += v0
		save v1 - v1

		v0 += 1
		mob-x += 1
		if mob-x == BOARD_WIDTH  then mob-y += 1
		if mob-x == BOARD_WIDTH  then mob-x := 0
		if mob-y != BOARD_HEIGHT then
	again
;
: board-draw
	plane 3
	v2 := VERT_OFFSET # y
	v3 := 0 # tile index
	loop
		v1 := HORIZ_OFFSET # v1 is x
		:calc LAST_COLUMN { HORIZ_OFFSET + BOARD_WIDTH * 7 }
		loop
			i := long board
			i += v3
			load v0
			i := long tiles
			i += v0
			sprite v1 v2 6
			v3 += 1
			v1 += 7
			if v1 != LAST_COLUMN then
		again
		v2 += 7
		if v2 != 64 then
	again

	plane 2
	mob-index := 0
	loop
		load-mob-regs
		if mob-type != MOB_TYPE_NONE then mob-draw
		mob-index += 1
		if mob-index != MOB_COUNT then
	again

	i := current-map
	load v0
	if v0 == 0 begin
		plane 1
		title-screen
		return
	end
	if v0 == 2 begin
		# level 2 is the randomly-generated "gameplay"
		# part of the game, so only draw the HUD there:
		health-draw
		score-draw
		return
	end
	if v0 == 3 begin
		plane 1
		chalice-screen
	end
;

# convert an x/y position into the address of a tile
: board-addr
	i  := long board
	i  += mob-x
	vd := mob-y # y * 1
	vd += vd    # y * 2
	vd += vd    # y * 4
	vd += vd    # y * 8
	vd += vd    # y * 16
	i  += vd
;

# if tile at x/y position is passable, return 0 in vf
: board-check
	board-addr
	load vd - vd
	vf := TILE_WALL
	vd -= vf
	if mob-x == -1           then vf := -1
	if mob-x == BOARD_WIDTH  then vf := -1
	if mob-y == -1           then vf := -1
	if mob-y == BOARD_HEIGHT then vf := -1
;

# if a mob is at x/y position, return its index in vf (-1 means none found)
# this routine is written in a somewhat contorted manner to make it usable
# from within mob planning routines without destroying active mob state:
: mob-check
	vf := 0
	loop
		if vf != mob-index begin # source mob != target mob
			i := mob-buffer
			i += vf
			i += vf
			i += vf
			i += vf
			load vd - vd
			if vd != MOB_TYPE_NONE begin
				vd := 1
				i += vd
				load vd - vd
				if vd == mob-x begin
					vd := 1
					i += vd
					load vd - vd
					if vd == mob-y then return
				end
			end
		end
		vf += 1
		if vf != MOB_COUNT then
	again
	vf := -1
;

# draw one tile at x/y position, using a tile index in vd
: board-draw-tile
	set-times-7 v0 mob-x
	set-times-7 v1 mob-y
	v0 += HORIZ_OFFSET
	v1 += VERT_OFFSET
	i  := long tiles
	i  += vd
	sprite v0 v1 6
;
# a more useful interface for the above subroutine;
# erase, modify, and redraw a board tile.
:macro replace-tile REPLACEMENT {
	board-addr
	load vd - vd
	plane 3
	board-draw-tile
	board-addr
	vd := REPLACEMENT
	save vd - vd
	board-draw-tile
	plane 2
}

###########################################
#
#  Random Level Generation
#
###########################################

:const CENTER_X         8
:const CENTER_Y         5
:const DRUNKARD_STEPS 120
:const MOB_GOAL         6 # (actual instances will be lower)

to-data

: random-deltas
	-1  0 # left
	-1  0 # left
	 1  0 # right
	 1  0 # right
	 0 -1 # up
	 0  1 # down
	 0  0 # n/a
	 0  0 # n/a

: random-mobs
	:byte TILE_ROACH_D
	:byte TILE_ROACH_U
	:byte TILE_ROACH_L
	:byte TILE_MOTH
	:byte TILE_MOTH
	:byte TILE_MOTH
	:byte TILE_MOTH
	:byte TILE_LLAMA

to-code

: gen-pick-tile
	loop
		v0 := random 0xFF
		if v0 > BOARD_END then
	again
;
: gen-place-tile # v1: tile, v3: valid target tile
	gen-pick-tile
	i := long board
	i += v0
	load vf - vf
	if vf == v3 then save v1 - v1
;
: gen-level
	# zero the board buffer
	v0 := 0
	v1 := 0
	v2 := 1
	i  := long board
	loop
		save v1 - v1
		i += v2
		v0 += 1
		if v0 != BOARD_SIZE then
	again

	# perform a drunkard's walk to lay down
	# a random pattern of contiguous floor tiles:
	mob-x := CENTER_X
	mob-y := CENTER_Y
	v2 := 0
	loop
		# random direction, biased horizontal
		i  := long random-deltas
		vf := random 0b1110
		i  += vf
		load v1
		# random step count 1-4,
		# to bias toward creating longer hallway-like structures
		v3 := random 0b11
		v3 += 1
		loop
			mob-x += v0
			mob-y += v1
			if mob-x == -1           then mob-x -= v0
			if mob-x == BOARD_WIDTH  then mob-x -= v0
			if mob-y == -1           then mob-y -= v1
			if mob-y == BOARD_HEIGHT then mob-y -= v1
			board-addr
			vf := TILE_FLOOR
			save vf - vf

			v3 += -1
			if v3 != 0 then
		again
		v2 += 1
		if v2 != DRUNKARD_STEPS then
	again

	# all empty tiles adjacent to floor tiles become wall tiles:
	mob-x := 0
	mob-y := 0
	loop
		board-addr
		load vf - vf
		if vf == TILE_NOTHING begin
			ve := 0
			:macro try-wall-adjacent SRC EDGE DELTA {
				if SRC != EDGE begin
					SRC += DELTA
					board-addr
					load vf - vf
					if vf == TILE_FLOOR then ve := 1
					:calc negdelta { - DELTA }
					SRC += negdelta
				end
			}
			try-wall-adjacent mob-x -1 -1
			try-wall-adjacent mob-x 15  1
			try-wall-adjacent mob-y -1 -1
			try-wall-adjacent mob-y  8  1
			if ve != 0 begin
				board-addr
				vf := TILE_WALL
				save vf - vf
			end
		end
		mob-x += 1
		if mob-x == BOARD_WIDTH then mob-y += 1
		if mob-x == BOARD_WIDTH then mob-x := 0
		if mob-y != BOARD_HEIGHT then
	again

	# first floor (raw) becomes player spawn:
	i  := long board
	vf := 1
	loop
		load v0 - v0
		while v0 != TILE_FLOOR
		i += vf
	again
	vf := TILE_PLAYER
	save vf - vf

	# last floor (raw) becomes stairs down:
	vf := BOARD_END
	loop
		i := long board
		i += vf
		load v0 - v0
		while v0 != TILE_FLOOR
		vf += -1
	again
	vf := TILE_STAIRS_DOWN
	save vf - vf

	# try until we succeed to place an alium:
	loop
		gen-pick-tile
		i := long board
		i += v0
		load vf - vf
		if vf != TILE_FLOOR then
	again
	vf := TILE_GARLIC
	save vf - vf
	# optionally, place a second alium:
	v1 := TILE_GARLIC
	v3 := TILE_FLOOR
	gen-place-tile

	# try a few times to place moths/roaches/llamas,
	# but it's OK if a given placement fails:
	v2 := 0
	loop
		i  := long random-mobs
		vf := random 0b111
		i  += vf
		load v1 - v1

		# llamas are impassable and indestructible, so if we deposit them
		# on a floor tile, there's a small chance we could block the path
		# to the exit tile. wall tiles on the other hand are by definition
		# adjacent to floor (and thus reachable), and replacing one will
		# leave access to the exit intact:
		v3 := TILE_FLOOR
		if v1 == TILE_LLAMA then v3 := TILE_WALL
		gen-place-tile
		v2 += 1
		if v2 != MOB_GOAL then
	again
;

###########################################
#
#  Items
#
###########################################

:macro bump-tile {
	# ve has the direction the source mob is moving
	# v2 has the source mob index (usually 0, the player)
	# v5-v7 have the source mob data,
	# except mob-x/mob-y are temporarily on the target tile.
	# all other non-global registers may be modified freely.

	board-addr
	load v0
	if v0 == TILE_GARLIC begin
		replace-tile TILE_FLOOR
		i := game-transition
		v0 := TRANSITION_ADD_SCORE
		save v0 - v0
		jump bump-tile-end
	end
	if v0 == TILE_STAIRS_DOWN begin
		i := current-map
		load v0 - v0
		if v0 == 2 begin
			i := player-score
			load v0 - v0
			if v0 >= SCORE_GOAL begin
				i := current-map
				v0 := 3
				save v0 - v0
			end
		else
			v0 += 1
			save v0 - v0
		end
		i := game-transition
		v0 := TRANSITION_GOTO_MAP
		save v0 - v0
		jump bump-tile-end
	end
	: bump-tile-end
}

###########################################
#
#  Mob Interaction
#
###########################################

:macro bump-mob {
	# ve has the direction the source mob is moving
	# vf has the target mob index
	# v2 has the source mob index (usually 0, the player)
	# v5-v7 have the source mob data,
	# except mob-x/mob-y are temporarily on the target tile.
	# all other non-global registers may be modified freely.

	v0 := mob-index # stash this temporarily
	mob-index := vf
	load-mob-regs # retrieve target

	if mob-type == MOB_TYPE_MOTH begin
		# obliterate moths on contact
		v1 := 1
		i  := mob-pow-table
		i  += mob-index
		save v1 - v1
		i  := mob-dead-table
		i  += mob-index
		save v1 - v1
	end
	if mob-type == MOB_TYPE_LLAMA begin
		# llamas fully heal the player
		i  := mob-hug-table
		i  += mob-index
		v1 := 1
		save v1 - v1
		i  := game-transition
		vf := TRANSITION_HEAL_PLAYER
		save vf - vf
	end

	mob-index := v0
}

###########################################
#
#  Mob Planning
#
###########################################

: deltas
	-1  0 # 0  left
	 1  0 # 1  right
	 0 -1 # 2  up
	 0  1 # 3  down

# reposition a mob based on a direction in ve
: mob-move-dir
	i := deltas
	i += ve
	i += ve
	load v0 - v1
	mob-x += v0
	mob-y += v1
;

# before animation for a turn begins, each mob
# plans its actions (independently) and sets up
# auxiliary tables with its decisions.
:macro mobs-plan {
	# compute a movement plan, and any side-effects, for every active mob.
	mob-index := 0
	loop
		load-mob-regs
		if mob-type != MOB_TYPE_NONE then mob-planner-dispatch
		i := mob-move-table
		i += mob-index
		save v0
		mob-index += 1
		if mob-index != MOB_COUNT then
	again

	# anything that is dead has its movement plan canceled:
	mob-index := 0
	loop
		load-mob-regs
		if mob-type != MOB_TYPE_NONE begin
			i := mob-dead-table
			i += mob-index
			load v0
			if v0 != 0 begin
				i := mob-move-table
				i += mob-index
				v0 := MOVE_NONE
				save v0
			end
		end
		mob-index += 1
		if mob-index != MOB_COUNT then
	again	
}

# the 'mob-planner' routines are all expected
# to leave mob-index intact and return the direction
# they wish to move (or -1) in v0.
# they are free to destroy the mob status registers:
: mob-planner-dispatch
	v0 := mob-type
	v0 += v0 # jump table entries are 2 bytes wide
	jump0 mob-planners
	: mob-planners
		jump mob-planner-player
		jump mob-planner-llama
		jump mob-planner-moth
		jump mob-planner-roach

: mob-planner-llama
	# llamas just sit there;
	# no planning necessary.
	v0 := -1
;

: mob-planner-player
	ve := 0 # direction
	loop
		if input == ve begin
			input := -1
			mob-move-dir
			mob-check
			if vf != -1 begin
				bump-mob
				v0 := ve
				v0 += BUMP_OFFSET
				return
			end
			board-check
			if vf != 0 begin
				bump-tile
				v0 := ve
				v0 += BUMP_OFFSET
				return
			end
			# move unimpeded in a direction:
			v0 := ve
			return
		end
		ve += 1
		if ve != 4 then
	again
	# this should not happen:
	:breakpoint unknown-player-input
	NOOP

: mob-planner-moth
	ve := random 0b11 # { 0, 1, 2, 3 }
	mob-move-dir
	# note: moths are small, so they don't bother
	# to check collision with other mobs.
	# larger mobs might want to via mob-check!
	board-check
	v0 := ve
	if vf != 0 then v0 := -1
;

to-data
: roach-turns
	# each entry is { turn RIGHT, STRAIGHT, turn LEFT, REVERSE }
	# you could easily create left-followers or other patterns
	# by altering this lookup table:
	2 0 3 1 # 0 facing left
	3 1 2 0 # 1 facing right
	1 2 0 3 # 2 facing up
	0 3 1 2 # 3 facing down
to-code

: mob-planner-roach
	# roaches attempt to follow the wall to their right:
	v3 := 0
	loop
		i := mob-roach-dir
		i += mob-index
		load ve - ve
		i  := long roach-turns
		ve += ve # *2
		ve += ve # *4
		i  += ve
		i  += v3
		load ve - ve # direction to try moving...
		mob-move-dir
		board-check
		while vf != 0
		mob-x -= v0
		mob-y -= v1
		v3 += 1
		if v3 != 4 then
	again
	i := mob-roach-dir
	i += mob-index
	save ve - ve
	v0 := ve
;

###########################################
#
#  Mob Animation
#
###########################################

to-data
: player-sprites
	sprite-begin
		0x00 0x28 0x38 0x68 0x74 0xBC 0x28
		0x28 0x38 0x68 0x74 0x7C 0x78 0x30
		0x00 0x28 0x38 0x68 0x74 0x7C 0x28
		0x28 0x38 0x28 0x74 0x7C 0x78 0x18
	sprite-end
: llama-sprites
	sprite-begin
		0x68 0xD0 0xF0 0x32 0x3E 0x3E 0x24
		0x60 0xD8 0xF0 0x32 0x3E 0x3E 0x24
		0x00 0x60 0xD8 0xF2 0x3E 0x3E 0x14
		0x60 0xD8 0xF0 0x32 0x3E 0x3E 0x24
	sprite-end
: moth-sprites
	sprite-begin
		0x00 0x00 0x50 0x20 0x00 0x00 0x00
		0x00 0x00 0x00 0x30 0x00 0x00 0x00
		0x00 0x14 0x08 0x00 0x00 0x00 0x00
		0x00 0x00 0x00 0x10 0x00 0x00 0x00
	sprite-end
: roach-sprites
	sprite-begin
		0x00 0x0A 0x0E 0xFA 0xFE 0x7C 0x54
		0x0A 0x0E 0x0A 0x3E 0xFC 0xFC 0x34
		0x00 0x0A 0x0E 0xFA 0xFE 0x7C 0x54
		0x00 0x80 0xEA 0xFE 0x7A 0x7E 0x68
	sprite-end

: mob-sprite-table
	pointer player-sprites
	pointer llama-sprites
	pointer moth-sprites
	pointer roach-sprites

# for each direction 0-3, and bump 4-7,
# four frames of x/y offsets from the mob's starting position:
: mob-animation-offsets
	-1  0  -3  0  -5  0  -7  0 # walk left
	 1  0   3  0   5  0   7  0 # walk right
	 0 -1   0 -3   0 -5   0 -7 # walk up
	 0  1   0  3   0  5   0  7 # walk down

	-1  0  -2  0  -1  0   0  0 # bump left
	 1  0   2  0   1  0   0  0 # bump right
	 0 -1   0 -2   0 -1   0  0 # bump up
	 0  1   0  2   0  1   0  0 # bump down

: heart # for hugs
	0x6C 0xFE 0xFE 0x7C 0x38 0x10
: smallheart # for HUD
	0x50 0xE8 0xF8 0x70 0x20
: pow-sprites # attack animation
	0x00 0x10 0x70 0x38 0x20 0x00 0x00
	0x12 0x78 0xD8 0x7C 0x78 0x24 0x80
	0x12 0x78 0xD8 0x7C 0x78 0x24 0x80
	0x00 0x10 0x70 0x38 0x20 0x00 0x00

to-code

: mob-draw
	i := long mob-sprite-table
	i += mob-type
	i += mob-type
	load v0 - v1
	i := mob-sprite-source
	save v0 - v1
	indirect mob-sprite-source

	i += mob-face
	v1 := timer
	v1 += mob-index # stagger mob animations
	v1 += mob-index # *2
	vf := 0b111 # modulo 8
	v1 &= vf
	v1 >>= v1
	set-times-7 v0 v1
	i += v0

	set-times-7 v0 mob-x
	set-times-7 v1 mob-y
	v0 += HORIZ_OFFSET
	v1 += VERT_OFFSET
	sprite v0 v1 7
;

# when mobs aren't taking a turn, just play an idle animation
:macro mobs-idle {
	vd := timer
	vd += 1
	if vd == 8 then vd := 0
	mob-index := 0
	loop
		load-mob-regs
		if mob-type != MOB_TYPE_NONE begin
			mob-draw
			swap timer vd
			mob-draw
			swap timer vd
		end
		mob-index += 1
		if mob-index != MOB_COUNT then
	again
	timer := vd
}

# drawing mobs is more complex during turn-taking,
# so we have an entirely separate drawing routine:
: mob-draw-with-anim
	i := mob-move-table
	i += mob-index
	load v3 - v3
	if v3 != MOVE_NONE begin
		if v3 == 0 then mob-face := FACE_LEFT
		if v3 == 4 then mob-face := FACE_LEFT
		if v3 == 1 then mob-face := FACE_RIGHT
		if v3 == 5 then mob-face := FACE_RIGHT
	end

	i := long mob-sprite-table
	i += mob-type
	i += mob-type
	load v0 - v1
	i := animated-mob-sprite-source
	save v0 - v1

	set-times-7 v0 mob-x
	set-times-7 v1 mob-y
	v0 += HORIZ_OFFSET
	v1 += VERT_OFFSET

	i  := long mob-animation-offsets
	v3 += v3
	i  += v3 # *2
	i  += v3 # *4
	i  += v3 # *6
	i  += v3 # *8
	i  += timer
	i  += timer
	load v3 - v3
	v0 += v3
	v3 := 1
	i  += v3
	load v3 - v3
	v1 += v3

	indirect animated-mob-sprite-source
	i  += mob-face
	sprite v0 v1 7
;

:macro mobs-animate {
	# erase normal sprites
	mob-index := 0
	loop
		load-mob-regs
		if mob-type != MOB_TYPE_NONE then mob-draw
		mob-index += 1
		if mob-index != MOB_COUNT then
	again

	# animate
	timer := 0
	loop
		mob-index := 0
		loop
			load-mob-regs
			if mob-type != MOB_TYPE_NONE begin
				# main tween
				if timer != 0 begin
					timer += -1
					mob-draw-with-anim
					timer += 1
				end
				mob-draw-with-anim

				# hit animation
				i := mob-pow-table
				i += mob-index
				load v3 - v3
				if v3 != 0 begin
					i := long pow-sprites
					i += timer
					i += timer
					i += timer
					i += timer
					i += timer
					i += timer
					i += timer
					sprite v0 v1 7
				end

				# hug animation
				i := mob-hug-table
				i += mob-index
				load v3 - v3
				if v3 != 0 begin
					v1 += -6
					v1 -= timer
					v1 -= timer
					i := long heart
					if timer != 0 then sprite v0 v1 6
					v1 += -2
					if timer != 3 then sprite v0 v1 6
				end

			end
			mob-index += 1
			if mob-index != MOB_COUNT then
		again
		wait

		# not buffering on the first few frames
		# provides a small natural debounce:
		if timer == 3 then buffer-input

		timer += 1
		if timer != 4 then
	again

	# erase animated sprites
	# note: this ensures that we clean up any inconsistencies
	# between mob-draw and mob-draw-with-anim;
	# otherwise we might leave behind junk pixels.
	# as an exercise for the tinkerer, try removing this pass!
	timer += -1
	plane 2
	mob-index := 0
	loop
		load-mob-regs
		if mob-type != MOB_TYPE_NONE then mob-draw-with-anim
		mob-index += 1
		if mob-index != MOB_COUNT then
	again
	timer := 0

	# finalize changes
	mob-index := 0
	loop
		load-mob-regs
		if mob-type != MOB_TYPE_NONE begin

			# update positions, facing
			i := mob-move-table
			i += mob-index
			load ve - ve
			if ve != MOVE_NONE begin
				v0 := MOVE_NONE
				save ve - ve
				if ve == 0 then mob-face := FACE_LEFT
				if ve == 4 then mob-face := FACE_LEFT
				if ve == 1 then mob-face := FACE_RIGHT
				if ve == 5 then mob-face := FACE_RIGHT
				if ve < BUMP_OFFSET then mob-move-dir

				if mob-index == 0 begin
					if ve < BUMP_OFFSET begin
						sfx sound-step
					else
						sfx sound-bump
					end
				end
			end

			# clear pow
			i := mob-pow-table
			i += mob-index
			v0 := 0
			save v0

			# clear hug
			i := mob-hug-table
			i += mob-index
			v0 := 0
			save v0

			# kill dead mobs
			i := mob-dead-table
			i += mob-index
			load v0 - v0
			if v0 != 0 begin
				v0 := 0
				save v0 - v0
				#mob-draw
				mob-type := MOB_TYPE_NONE
			else
				mob-draw
			end

			# if mobs overlap player, they inflict damage:
			if mob-index != 0 begin
				mob-check
				if vf == 0 begin
					i := game-transition
					vf := TRANSITION_DAMAGE_PLAYER
					save vf - vf
				end
			end

			save-mob-regs
		end
		mob-index += 1
		if mob-index != MOB_COUNT then
	again
}

###########################################
#
#  Transition Events
#
###########################################

# note: it is materially important that these are inlined as part
# of the game's main loop. some transitions could end the game (or better, or worse)
# and need to re-initialize the game. if this occurs with any subroutine calls
# pending on the stack, we'll leave their return address behind, leading
# to a game that crashes after a few plays.
# this is also a large part of the motivation of having a deferred transition
# mechanism instead of allowing mobs/items/etc to directly end the game under their own power.

:macro transitions {
	i := game-transition
	load v0
	if v0 == TRANSITION_GOTO_MAP begin
		plane 3
		clear
		timer := 0
		i := current-map
		load vf - vf
		if vf != 2 begin
			board-unpack
		else
			gen-level
			unpack16 board
			board-unpack-raw
		end
		board-draw
		i := game-transition
		v0 := 0
		save v0
	end
	if v0 == TRANSITION_HEAL_PLAYER begin
		sfx sound-heal
		health-draw
		health-reset
		health-draw
		i := game-transition
		v0 := 0
		save v0
	end
	if v0 == TRANSITION_DAMAGE_PLAYER begin
		sfx sound-hurt
		health-decrement
		i := game-transition
		v0 := 0
		save v0
		i := player-health
		load v0 - v0
		if v0 == 0 begin
			plane 3
			clear
			plane 1
			game-over-screen
			wait-for-space
			jump main
		end
		v0 := 0
	end
	if v0 == TRANSITION_ADD_SCORE begin
		sfx sound-pickup
		i := game-transition
		v0 := 0
		save v0
		i := player-score
		load v0 - v0
		if v0 >= SCORE_GOAL begin
			i := current-map
			load vf - vf
			if vf == 3 begin
				# you collected the final alium!
				plane 3
				clear
				plane 1
				you-win-screen
				wait-for-space
				jump main
			else
				score-increment
			end
		else
			score-increment
		end
		v0 := 0
	end
}

:macro debug-keys {
	vf := OCTO_KEY_1  if vf key begin
		i := game-transition
		vf := TRANSITION_DAMAGE_PLAYER
		save vf - vf
	end
	vf := OCTO_KEY_2  if vf key begin
		i := game-transition
		vf := TRANSITION_ADD_SCORE
		save vf - vf
	end
}

###########################################
#
#  Main Loop
#
###########################################

: main
	hires

	plane 3
	clear
	i  := current-map
	vf := 0
	save vf - vf
	input := -1
	timer := 0
	health-reset
	score-reset
	board-unpack
	board-draw

	loop
		transitions

		plane 2
		if input != -1 begin
			mobs-plan
			mobs-animate
		else
			mobs-idle
		end

		# note: remove these before shipping the game, probably:
		debug-keys

		loop
			buffer-input
			vf := delay
			if vf != 0 then
		again
		vf := 5
		delay := vf
	again
